﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using Loki.Engine;
using Loki.Messages;
using Loki.Utils;

namespace Loki
{
    public class LokiTaskService : LokiService, ITaskService
    {
        private Dictionary<Guid, BackgroundWorker> _DeclaredWorkers = new Dictionary<Guid, BackgroundWorker>();
        private object _WorkerLock = new object();
        private void CleanWorker(Guid P_ID)
        {

        }


        private LokiCollection<TaskStatus> _Status = new LokiCollection<TaskStatus>();
        private ReaderWriterLockSlim _StatusLock = new ReaderWriterLockSlim();
        private void StartWork(Guid P_ID, string P_Status)
        {
            _StatusLock.WithWrite(() =>
            {
                _Status.Add(new TaskStatus() { ID = P_ID, Status = P_Status, Percentage = 0 });
            });
        }

        private void UpdateWork(Guid P_ID, string P_Status, double P_Percentage)
        {
            TaskStatus L_Status = _Status.FirstOrDefault(x => x.ID == P_ID);
            if (L_Status != null)
            {
                L_Status.Status = P_Status;
                L_Status.Percentage = P_Percentage;
            }
        }

        private void EndWork(Guid P_ID)
        {
            /*_StatusLock.WithWrite(() =>
            {*/
            TaskStatus L_Status = _Status.FirstOrDefault(x => x.ID == P_ID);
            if (L_Status != null)
                _Status.Remove(L_Status);
            //});
        }

        public ILokiEnumerable<ITaskStatus> WorkingProcess
        {
            get
            {
                /*return _StatusLock.WithRead(() =>
                {*/
                return _Status;
                //});/
            }
        }

        public ITaskStatus GetStatus(ITask P_Task)
        {
            /*return _StatusLock.WithRead(() =>
            {*/
            return _Status.FirstOrDefault(x => x.ID == P_Task.ID);
            //});
        }

        #region ITaskService Members

        public ITask CreateWorker(string P_Title, Action P_DoWork, Action P_OnComplete, Action<Exception> P_OnError)
        {
            LokiTask L_Task = new LokiTask(new BackgroundWorker());
            L_Task.Worker.Disposed += (s, e) => CleanWorker(L_Task.ID);

            // insert for disposing
            RegisterDisposable(L_Task);

            if (P_DoWork != null)
                L_Task.Worker.DoWork += delegate(object sender, DoWorkEventArgs e) { P_DoWork(); };
            if (P_OnComplete != null)
                L_Task.Worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
                {
                    EndWork(L_Task.ID);
                    if (e.Error == null)
                        P_OnComplete();
                    else if (P_OnError != null) P_OnError(e.Error);
                    else
                        throw BuildErrorFormat<LokiException>(ToolsErrors.TOO_501, P_Title);
                };

            L_Task.Run = () => { if (!L_Task.Worker.IsBusy) { StartWork(L_Task.ID, P_Title); L_Task.Worker.RunWorkerAsync(); } };
            return L_Task;
        }



        public ITaskWithUpdate CreateUpdatetableWorker(string P_Title, Action<ITaskUpdater> P_DoWork, Action P_OnComplete, Action<Exception> P_OnError)
        {
            throw new NotImplementedException();
        }

        public ITask<TArgument> CreateWorker<TArgument, TResult>(string P_Title, Func<TArgument, TResult> P_DoWork, Action<TResult> P_OnComplete, Action<Exception> P_OnError)
        {
            LokiTask<TArgument> L_Task = new LokiTask<TArgument>(new BackgroundWorker());
            L_Task.Worker.Disposed += (s, e) => CleanWorker(L_Task.ID);

            // insert for disposing
            RegisterDisposable(L_Task);

            if (P_DoWork != null)
                L_Task.Worker.DoWork += delegate(object sender, DoWorkEventArgs e) { e.Result = (TResult)P_DoWork((TArgument)e.Argument); };
            if (P_OnComplete != null)
                L_Task.Worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
                {
                    EndWork(L_Task.ID);
                    if (e.Error == null)
                        P_OnComplete((TResult)e.Result);
                    else if (P_OnError != null) P_OnError(e.Error);
                    else
                        throw BuildErrorFormat<LokiException>(ToolsErrors.TOO_501, P_Title);
                };

            L_Task.Run = (a) => { if (!L_Task.Worker.IsBusy) { StartWork(L_Task.ID, P_Title); L_Task.Worker.RunWorkerAsync(a); } };
            return L_Task;
        }

        public ITask<TArgument> CreateUpdatetableWorker<TArgument, TResult>(string P_Title, Action<ITaskUpdater, TArgument, TResult> P_DoWork, Action<TResult> P_OnComplete, Action<Exception> P_OnError)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Dispose
        private struct DisposableSet
        {
            public IDisposable Item;
            public Action<IDisposable> Unbinder;
        }

        private List<DisposableSet> _Disposables = new List<DisposableSet>();

        protected void RegisterDisposable(IDisposable P_Disposable, Action<IDisposable> P_Unbind = null)
        {
            _Disposables.Add(new DisposableSet() { Item = P_Disposable, Unbinder = P_Unbind });
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                foreach (var L_Item in _Disposables)
                {
                    if (L_Item.Unbinder != null)
                        L_Item.Unbinder(L_Item.Item);
                    L_Item.Item.Dispose();
                }
                _Disposables.Clear();
                _Status.Dispose();
                _StatusLock.Dispose();
            }
            base.Dispose(disposing);
        }
        #endregion

        public override string Name
        {
            get { return "Loki task service"; }
        }

        public override Guid ID
        {
            get { return new Guid("{85EF0614-7448-4699-AACF-663455190045}"); }
        }
    }
}
